feat: agent background edits on notebooks#586
Open
emrberk wants to merge 25 commits into
Open
Conversation
…CellType fallback ladder Completes Phase 5's read-method carve-out. createModelToolsClient is the sole client and always provides getCellBasics, so the per-field getCellSql/getCellType fallbacks in dispatch and applyNotebookState were unreachable in production. Removes the interface optionals, both method bodies, the orphaned findCellSoft helper, and rewrites the tests that relied on the fallback onto getCellBasics. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KQfYWS6dFLNBxZ2ncyTNyW
…e-export cleanup Un-exports six notebooks/ module symbols referenced only within their own file, removes the always-false `activate` knob from the create/duplicate options and both call sites, fixes four import-then-re-export cases by pointing consumers at each symbol's real source, repairs a leaked comment fragment in bufferOwnership, and removes the obsolete SIMPLIFY_PLAN.md (UNIFY_PLAN.md already gone). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KQfYWS6dFLNBxZ2ncyTNyW
On a mounted notebook, the live runCell wrapper inferred its result by object-identity diff against the pre-run result. When a user's manual Run superseded the agent's in-flight run, the cell then held the user's result, which the wrapper misattributed to the agent — returning success:false/pending with no unverified note for a write that had actually committed, so the agent could re-run it and duplicate the DML. Thread a `superseded` flag out of the live run (useCellExecution → provider runCell) as a `CellRunOutcome`, and on supersession have the controller return the SUPERSEDED_RUN_NOTE the headless path already emits, instead of reading the newer run's result back. Adds Given/When/Then tests for the live runCell supersede, normal-record, and no-op cases. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KQfYWS6dFLNBxZ2ncyTNyW
…tch (phase 5) Lands phase 5 additively on top of the #1 supersede fix and the phase-6 sweep, reconciled so both survive. - NotebookController -> { bufferId, kind, mutate, readView, runCell }; drop the named ops and makeNotebookControllerOps; dispatch builds a transition and calls withBoundNotebook(ctrl.mutate) / readView directly - strip ModelToolsClient to the quest + workspace surface; delete the isLiveController pre-checks (validation already lives in the transitions) - move read serializers into notebookSnapshot; buildSnapshot reads via readView - preserve the live runCell CellRunOutcome/superseded reporting from the #1 fix in the collapsed wrapper, plus its re-homed unknown_cell guard - keep the sweep's NotebookToolError source import and the dropped activate knob Original phase-5 authored on machine A; reconciled onto origin here. Co-Authored-By: emrberk <emreberk5699@gmail.com> Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KQfYWS6dFLNBxZ2ncyTNyW
list_cells (summarizeCells) omitted the cell `name`, while get_cell and get_notebook_state return it — an inconsistency a model/caller could trip on when correlating cells by name. Surface `name` on NotebookCellSummary when set. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KQfYWS6dFLNBxZ2ncyTNyW
…ss the read-only gate A write the validator rejects (e.g. a type-mismatched INSERT) returns an error result, so classifyStatements maps it to `ERROR`, not `DDL_DML`. The permission gates only denied on `DDL_DML`, so an ERROR-classified write PASSED the gate and was sent to /exec — verified live via MCP: a read-only agent's INSERT reached the server (blocked only by demo's own 403; a writable instance would have run it). Fail closed: under read-only permission, only confirmed DQL may execute. Any statement the validator can't confirm as a read (klass ERROR) is treated as a possible write — run_query / run_cell deny it, add_cell auto-run skips it. The validator's error is surfaced in the message so the agent knows why. classifyStatements now carries the validator error on ERROR statements. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KQfYWS6dFLNBxZ2ncyTNyW
…n't bypass the read-only gate" This reverts commit 0569fd4.
…ssive-notebook-edits
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Depends on #581
Tandem PR: questdb/mcp-bridge#1
What
Agents could previously only edit the notebook the user had open, and was stealing focus of the users while editing a notebook. This PR lets the MCP bridge and the built-in assistant work on any notebook in the background.
activate_notebooktool for navigating the user if requestedHow it works
notebookDexieController.ts): every op on an unmounted buffer is a queued read → pure transition → write on the persisted buffer row. No mounting, no hidden React tree.notebookBufferQueue.ts): all writers (agent ops, mount seed read, unmount flush, debounced persists) are strictly ordered per buffer — no lost updates at the mount/unmount boundaries.mounted_mid_editerror telling the agent to re-sync and retry.run_cell(notebookHeadlessRun.ts): runs cells on unmounted notebooks with a verification outcome machine (notebook/cell deleted, user mounted mid-run, cell changed mid-run, superseded) — results are persisted and reported asunverifiedwith a note when the run can't be attributed cleanly.STATE_STALEif the user edited the notebook after the agent's last read (per-buffer action sequence, baseline captured before the read).apply_notebook_stateis deliberately not up-front gated — it's a wholesale PUT; it keeps its internal mid-apply staleness re-check.activate_notebooktool: brings a buffer to the foreground and reveals a cell (used on user request; intentionally ungated — accepted risk).Testing
agentBackgroundEdits.spec.js+ sharedmcpFakeWebSocket.jstest util.